package com.ght.springboot.elasticsearch;

import com.ght.springboot.api.query.ElasticSearchQuery;
import com.ght.springboot.elasticsearch.domain.BaseMapping;
import com.ght.springboot.elasticsearch.domain.BookCityMapping;
import com.ght.springboot.elasticsearch.domain.FieldAttribute;
import com.ght.springboot.eunm.elasticsearch.BulkMethodEnum;
import com.ght.springboot.eunm.elasticsearch.QueryMethodEnum;
import com.ght.springboot.util.AssertUtil;
import com.ght.springboot.util.MyEasyJsonUtil;
import com.ght.springboot.vo.CommentBookVo;
import com.ght.springboot.vo.ResultDocs;
import com.ght.springboot.vo.ZDocVo;

import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.core.*;
import io.searchbox.indices.CreateIndex;
import io.searchbox.indices.DeleteIndex;
import io.searchbox.indices.mapping.GetMapping;
import io.searchbox.indices.mapping.PutMapping;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: Elasticsearch 基础查询
 * @author: haitao.gao
 * @create: 2019-02-25 10:23
 **/
public class BaseOperates<T> {

    Logger logger  = LoggerFactory.getLogger(getClass());

    /**
     * Jest 客户端
     */
    public static JestClient jestClient;

    /**
     * 属性查询前缀
     */
    private static List<String> prefix = Arrays.asList("must","could","mustNot");

    public BaseOperates(){
        if(jestClient == null){
            jestClient = ElasticsearchDsCfg.elasticsearchDsCfg.getClient();
        }
    }

    /**
     * 获取索引映射
     */
    public void getIndex(String index,String type){
        GetMapping.Builder builder = new GetMapping.Builder();
        builder.addIndex(index).addType(type);
        try {
            JestResult jestResult = jestClient.execute(builder.build());
            AssertUtil.isTrue(jestResult.isSucceeded(),"Elasticsearch getIndex is error："+jestResult.getErrorMessage());
        }catch (Exception e){
            AssertUtil.isTrue(false,e.getMessage());
        }
    }

    /**
     * 删除索引
     * @param index
     */
    public void deleteIndex(String index){

        try {
            JestResult jestResult = jestClient.execute(new DeleteIndex.Builder(index).build() );
            AssertUtil.isTrue(jestResult.isSucceeded(),"Elasticsearch deleteIndex is error："+jestResult.getErrorMessage());
        }catch (Exception e){
            AssertUtil.isTrue(false,e.getMessage());
        }
    }

    /**
     * 指定Mapping创建索引
     * @param index
     * @param type
     * @param mapping
     */
    public void createIndexBymapping(String index,String type,String mapping){

        // PUT http://ip:port/index/type/_mapping
        PutMapping putMapping = new PutMapping.Builder(index,type,mapping).build();
        try{
            JestResult jestResult = jestClient.execute(putMapping);
            AssertUtil.isTrue(jestResult.isSucceeded(),"Elasticsearch createIndex Error："+jestResult.getErrorMessage());
        }catch (Exception e){
            AssertUtil.isTrue(false,e.getMessage());
        }

    }

    /**
     * 修改Mapping映射（勿用）
     */
    public void updateIndexMapping(String index,String type,String mapping){

    }

    /**
     * 删除Type(勿用)
     */
    public void deleteIndexMapping(String index,String type){
        DeleteIndex deleteIndex = new DeleteIndex.Builder(index).type(type).build();
        try{
            JestResult jestResult = jestClient.execute(deleteIndex);
            System.out.print(jestResult.getJsonObject().toString());
        }catch (Exception e){
            System.out.print(e);
        }
    }

    /**
     * 创建索引
     * @param indexName
     * @param type
     */
    public void createIndex(String indexName,String type){
        try {
            JestResult jestResult = jestClient.execute(new CreateIndex.Builder(indexName).build());
            AssertUtil.isTrue(jestResult.isSucceeded(),"Elasticsearch createIndex is error："+jestResult.getErrorMessage());
        }catch (Exception e){
            AssertUtil.isTrue(false,e.getMessage());
        }
    }

    /**
     * 插入单条数据
     * @param indexName
     * @param typeName
     * @param date
     */
    public void insert(String indexName,String typeName,String date){

        Index index = new Index.Builder(date).index(indexName).type(typeName).build();
        try {
            JestResult jestResult = jestClient.execute(index);
            AssertUtil.isTrue(jestResult.isSucceeded(),"Elasticsearch insertDoc is error："+jestResult.getErrorMessage());
        }catch (Exception e){
            AssertUtil.isTrue(false,e.getMessage());
        }

    }

    /**
     *
     * @param indexName 索引
     * @param typeName  类型
     * @param OperateType 批量操作类型
     * @param id        文档ID
     * @param dateList   操作数据
     */
    public void bulk(String indexName,String typeName,String OperateType,String id,List<String> dateList){
        Bulk.Builder bulkBuilder = new Bulk.Builder().defaultIndex(indexName).defaultIndex(typeName);
        dateList.forEach(date->{
            if(OperateType.equals(BulkMethodEnum.INSERT.getType())){
                bulkBuilder.addAction(new Index.Builder(date).index(indexName).type(typeName).build());
            }
            else if(OperateType.equals(BulkMethodEnum.UPDATE.getType())){
                bulkBuilder.addAction(new Update.Builder(date).index(indexName).type(typeName).id(id).build());
            }
        });
        if(OperateType.equals(BulkMethodEnum.DELETE.getType())){
            bulkBuilder.addAction(new Delete.Builder(null).index(indexName).type(typeName).id(id).build());
        }
        try {
            BulkResult bulkResult = jestClient.execute(bulkBuilder.build());
            if(!bulkResult.isSucceeded()){
                AssertUtil.isTrue(false,"批量操作异常："+bulkResult.getErrorMessage());
            }
        }catch (Exception e){
            logger.error("批量操作异常"+e);
            AssertUtil.isTrue(false,e.getMessage());
        }
    }

    /**
     * 单文档删除
     * @param indexName
     * @param typeName
     * @param id
     */
    public void deleteDoc(String indexName,String typeName,String id){
        Delete delete = new Delete.Builder(id).index(indexName).type(typeName).build();
        try{
            DocumentResult documentResult = jestClient.execute(delete);
            AssertUtil.isTrue(false,"单文档删除异常："+documentResult.getErrorMessage());
        }catch (Exception e){
            logger.error("单文档删除异常"+e);
            AssertUtil.isTrue(false,e.getMessage());
        }
    }

    /**
     * 单文档更新
     * @param indexName
     * @param typeName
     * @param id
     * @param content
     */
    public void updateDoc(String indexName,String typeName,String id,String content){
        String updateRequest = "{\"doc\": " + content + "}";
        Update update = new Update.Builder(updateRequest).index(indexName).type(typeName).id(id).build();
        try{
            DocumentResult documentResult = jestClient.execute(update);
            AssertUtil.isTrue(documentResult.isSucceeded(),"Elasticsearch is error："+documentResult.getErrorMessage());
        }catch (Exception e){
            logger.error("单文档更新异常"+e);
            AssertUtil.isTrue(false,e.getMessage());
        }
    }

    /**
     * 删除所查找内容(勿用)
     * @param indexName
     * @param typeName
     * @param queryJson
     */
    public void deleteByQuery(String indexName,String typeName,String queryJson){
        if(StringUtils.isBlank(queryJson)){
//            queryJson = "{\"query\":{\"match_all\":{}}}";
        }
        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(queryJson).addIndex(indexName).addType(typeName).build();
        try{
            JestResult jestResult = jestClient.execute(deleteByQuery);
            AssertUtil.isTrue(jestResult.isSucceeded(),"Elasticsearch is error："+jestResult.getErrorMessage());
        }catch (Exception e){
            logger.error("删除所查找内容异常"+e);
            AssertUtil.isTrue(false,e.getMessage());
        }

    }

    /**
     * 简单批量化获取(可以指字段)
     * @param zDocList
     * @param result
     * @param <T>
     * @return
     */
    public List<T> mget(List<ZDocVo> zDocList, Class<? extends ResultDocs> result){
        MultiGet multiGet = new MultiGet.Builder.ByDoc(zDocList).build();
        try{
            JestResult jestResult = jestClient.execute(multiGet);
            System.out.print(jestResult.getJsonString());
            ResultDocs<T> resultDocs = MyEasyJsonUtil.string2json(jestResult.getJsonString(),result);
            return resultDocs.getDocsList().stream()
                    .filter(doc -> doc.getFound().equals(true))
                    .map(doc->doc.getSource())
                    .collect(Collectors.toList());
        }catch (Exception e){
            logger.error("Elasticsearch mget is error" + e);
            return new ArrayList<>();
        }
    }

//    public <T>List<T> get(String index,String type,String id){
//
//        Get get = new Get();
//
//    }

    /**
     *  _search 查询
     * @param query
     * @return
     * @throws IllegalAccessException
     */
    public <T> List<SearchResult.Hit<T, Void>> search(ElasticSearchQuery query){

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        SearchResult searchResult = null;
        prefix.forEach(prefixName->{
            Boolean isSuccess = converParam(queryBuilder,prefixName,query);
            AssertUtil.isTrue(isSuccess,"Elasticsearch 查询转换异常");
        });

        if(query.getPageNo() != null){
            searchSourceBuilder.query(queryBuilder).size(query.getPageSize()).from((query.getPageNo()-1)*query.getPageSize());
        }
        else {
            searchSourceBuilder.query(queryBuilder).size(500);
        }
        Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(query.getIndex()).addType(query.getType()).build();

        try {
            searchResult = jestClient.execute(search);
            AssertUtil.isTrue(searchResult.isSucceeded(),searchResult.getErrorMessage());
        }catch (Exception e){
            logger.error("elasticsearch search is error, reason is " +e);
            AssertUtil.isTrue(false,e.getMessage());
        }
        return searchResult.getHits((Class<T>) new Object().getClass());
    }

    /**
     * 转换查询参数格式
     * @param queryBuilder
     * @param type
     * @param query
     * @throws IllegalAccessException
     */
    public Boolean converParam(BoolQueryBuilder queryBuilder, String type, ElasticSearchQuery query) {

        try {

            if(query.getMust() != null && type.equals(QueryMethodEnum.MUST.getMethod())){
                Field[] fields = query.getMust().getClass().getDeclaredFields();
                for(Field field : fields){
                    field.setAccessible(true);
                    if(field.get(query.getMust())!=null && StringUtils.isNotBlank(field.get(query.getMust()).toString())){
                        queryBuilder.must(QueryBuilders.matchPhraseQuery(field.getName(),field.get(query.getMust())));
                    }
                }
            }
            else if(query.getMustNot() != null && type.equals(QueryMethodEnum.MUST_NOT.getMethod())){
                Field[] fields = query.getMustNot().getClass().getDeclaredFields();
                for(Field field : fields){
                    field.setAccessible(true);
                    if(field.get(query.getMust())!=null && StringUtils.isNotBlank(field.get(query.getMust()).toString())){
                        queryBuilder.mustNot(QueryBuilders.matchPhraseQuery(field.getName(),field.get(query.getMust())));
                    }
                }
            }
            else if(query.getShould() != null && type.equals(QueryMethodEnum.SHOULD.getMethod())){
                Field[] fields = query.getShould().getClass().getDeclaredFields();
                for(Field field : fields){
                    field.setAccessible(true);
                    if(field.get(query.getMust())!=null && StringUtils.isNotBlank(field.get(query.getMust()).toString())){
                        queryBuilder.should(QueryBuilders.matchPhraseQuery(field.getName(),field.get(query.getMust())));
                    }
                }
            }
        }catch (IllegalAccessException e){
            logger.error("Elasticsearch 转换查询参数异常：" + e);
            return false;
        }
        return true;
    }

    public static void main(String[] args) {

        BaseOperates<CommentBookVo> baseOperates = new BaseOperates<>();
//        baseOperates.createIndex("book_city","Lyq");
//        BookCityMapping bookCityMapping = new BookCityMapping(
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("date","english",1,"yyyy-MM-dd"),
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("text","english",1),
//                new FieldAttribute("text","english",1)
//        );
//        BaseMapping baseMapping = new BaseMapping(bookCityMapping);
////        baseOperates.createIndexBymapping("book_city","Lyq",MyEasyJsonUtil.json2string(baseMapping));
//        baseOperates.search()
    }


}
